import pathlib
from typing import Annotated

import qrcode
from fastapi import Depends
from sqlalchemy import Integer, delete, func, insert, update

from forward.api.admin.orm.lucky.activity import ActivityORM
from forward.api.admin.orm.lucky.activity_code_no import ActivityCodeNoORM
from forward.api.admin.orm.lucky.activity_prize import ActivityPrizeORM
from forward.api.admin.orm.lucky.code import CodeORM
from forward.api.admin.orm.lucky.order import ActivityOrderORM
from forward.api.admin.schemas.lucky.activity import (
    ActivityAddInSchema,
    ActivityDeleteInSchema,
    ActivityDetailInfoSchema,
    ActivityDetailInSchema,
    ActivityDetailOutSchema,
    ActivityEditInSchema,
    ActivityListInSchema,
    ActivityStatusInSchema,
)
from forward.api.admin.schemas.lucky.activity_code_no import (
    ActivityCodeNoDetailOutSchema,
)
from forward.api.admin.service.lucky.code import CodeService
from forward.api.admin.service.lucky.draw import DrawService
from forward.api.h5.orm.lucky.draw import DrawORM
from forward.common.enums import ActivityTypeEnum, CodeStatusEnum
from forward.config import get_settings
from forward.dependencies.database import db
from forward.exceptions.base import AppException
from forward.extend.pydantic.result_api import ResultAPI
from forward.http_base import HttpResp
from forward.model.schemas.lucky.activity import ActivityCodeNoOutSchema
from forward.model.tables.activity import (
    ActivityModel,
    ActivityOrderModel,
    ActivityPrizeModel,
)
from forward.utils.config import ConfigUtil
from forward.utils.urls import UrlUtil


class ActivityService:
    def __init__(
        self,
        draw_service: Annotated[DrawService, Depends()],
        code_service: Annotated[CodeService, Depends()],
        activity_orm: Annotated[ActivityORM, Depends()],
        activity_order_orm: Annotated[ActivityOrderORM, Depends()],
        code_orm: Annotated[CodeORM, Depends()],
        activity_code_no_orm: Annotated[ActivityCodeNoORM, Depends()],
        activity_prize_orm: Annotated[ActivityPrizeORM, Depends()],
        draw_orm: Annotated[DrawORM, Depends()],
    ):
        self.draw_service = draw_service
        self.qr_code_path = "static" + "/" + "qrcode"
        self.activity_orm = activity_orm
        self.code_orm = code_orm
        self.activity_order_orm = activity_order_orm
        self.code_service = code_service
        self.activity_code_no_orm = activity_code_no_orm
        self.activity_prize_orm = activity_prize_orm
        self.draw_orm = draw_orm

    async def lists(self, list_in: ActivityListInSchema):
        where = []
        if list_in.merchant_id:
            where.append(ActivityModel.merchant_id == list_in.merchant_id)
        if list_in.status is not None:
            where.append(ActivityModel.status == list_in.status)
        pagination = await self.activity_orm.lists(
            page_num=list_in.page_no,
            page_size=list_in.page_size,
            where=where,
        )

        lists = []
        # 处理返回数据
        for item in pagination.lists:
            data = item.model_dump()
            draw = await self.draw_orm.get_one_by_id(item.draw_id)
            data["lock_text"] = "作废" if item.lock else ""
            if item.lock == 1 and item.title:
                data["title"] = "【已作废】" + item.title
            data["residue_count"] = await self.get_activity_residue_count(
                activity_id=item.id
            )
            data["type_text"] = ActivityTypeEnum.get_label(
                item.type if item.type else 0)+f"【{draw.title if  draw else ''}】"
            data["qrcode_img_url"] = await UrlUtil.to_absolute_url(
                f"static/qrcode/{item.id}.png"
            )
            data["guaguale_url"] = await ConfigUtil.get_val("h5_channel", "domain", "")
            data["guaguale_qrcode_url"] = await UrlUtil.to_absolute_url(await ConfigUtil.get_val("h5_channel", "domainQrcode", "") or "")
            h5_domain = await ConfigUtil.get_val("h5_channel", "domain", "")
            draw_path = draw.path if draw else ""
            if draw_path and h5_domain:
                data["qrcode_redirect_url"] = (
                    h5_domain + draw_path + f"?activityId={item.id}"
                )
            # 获取关联奖品
            activity_prize_list = await self.activity_prize_orm.get_all_by_filters(
                activity_id=item.id
            )

            # 获取关联的码号批次
            activity_code_no_list = await self.activity_code_no_orm.get_all_by_filters(
                activity_id=item.id
            )
            activity_code_no_list_data = [
                activity_code_no.model_dump()
                for activity_code_no in activity_code_no_list
            ]
            for item in activity_code_no_list_data:
                item["text"] = (
                    f"{item['prefix']}.{item['code_id_start']}~{item['code_id_end']}  计数：{item['total_num']}"
                )
            data["activity_code_no_list"] = activity_code_no_list_data
            data["activity_prize_list"] = activity_prize_list

            activity = ActivityDetailOutSchema(**data)

            lists.append(activity)
        return ResultAPI(data={"lists": lists, "count": pagination.total})

    async def add(self, add_in: ActivityAddInSchema):

        # 活动类型 1刮刮卡 2链接二维码
        if add_in.type == ActivityTypeEnum.GUAGUALE:
            return await self.add_guaguale_activity(add_in)
        elif add_in.type == ActivityTypeEnum.QRCODE:
            return await self.add_qr_activity(add_in)

    async def add_qr_activity(self, add_in: ActivityAddInSchema):
        """
        二维码活动
        """
        activity_prize_list = add_in.activity_prize_list
        # 创建活动
        data = add_in.model_dump(exclude={"activity_prizes"})
        if add_in.rules_desc:
            data.update(
                {
                    "rules_desc": ",".join(
                        item
                        for item in add_in.rules_desc
                        if item is not None and item != ""
                    )
                }
            )
        # statement = insert(ActivityModel).values(**activity)
        # activity_id = await db.execute(statement)
        activity = await self.activity_orm.create(data)
        if not activity:
            raise AppException(exc=HttpResp.FAILED, msg="创建活动失败")

        # 创建二维码
        h5_domain = await ConfigUtil.get_val("h5_channel", "domain", "")
        draw_id = add_in.draw_id
        draw = await self.draw_service.one_by_id(draw_id) if draw_id else None
        draw_path = draw.path if draw else ""
        url = f"{h5_domain}{draw_path}?activityId={activity.id}"
        img = qrcode.make(url)
        fload = "qrcode"
        save_path = pathlib.Path(get_settings().static_directory, fload)
        print("save_path", save_path)
        save_path.mkdir(parents=True, exist_ok=True)
        file_name = save_path / f"{activity.id}.png"
        with open(file_name, "wb") as f:
            img.save(f)
            f.close()
        # 保存二维码链接
        await self.activity_orm.update(
            id=activity.id,
            data={"qrcode_img_path": f"static/{fload}/{activity.id}.png"},
        )
        # 创建活动附属表
        # 奖品
        activity_prize_list_data = []
        for activity_prize in activity_prize_list:
            data = activity_prize.model_dump()
            data["activity_id"] = activity.id
            activity_prize_list_data.append(data)
        await self.activity_prize_orm.bulk_create(activity_prize_list_data)

        return ResultAPI()

    # async def add_guaguale_activity_(self, add_in: ActivityAddInSchema):
    #     """
    #     刮刮卡活动(暂时不用)
    #     """
    #     prefix = add_in.prefix
    #     start_code_id = add_in.start_code_id
    #     end_code_id = add_in.end_code_id
    #     activity_num = add_in.num
    #     # 1检测码号是否可用
    #     if not prefix or not start_code_id or not end_code_id:
    #         raise AppException(exc=HttpResp.FAILED, msg="卡编码参数错误")
    #     await self.code_service.check_code_is_empty(
    #         prefix=prefix, start_code_id=start_code_id, end_code_id=end_code_id
    #     )

    #     # 创建活动
    #     data = add_in.model_dump(exclude={"activity_prizes"})
    #     if add_in.rules_desc:
    #         data.update(
    #             {
    #                 "rules_desc": ",".join(
    #                     item
    #                     for item in add_in.rules_desc
    #                     if item is not None and item != ""
    #                 )
    #             }
    #         )
    #     # 创建活动
    #     acitvity = await self.activity_orm.create(data)
    #     if not acitvity:
    #         raise AppException(exc=HttpResp.FAILED, msg="创建失败")

    #     # 创建中间表（活动和奖品）
    #     activity_prizes = add_in.activity_prizes
    #     await self.relation_activity_prizes(acitvity.id, activity_prizes)

    #     #  关联码号奖品
    #     if not prefix or not start_code_id or not end_code_id:
    #         raise AppException(exc=HttpResp.FAILED, msg="卡片参数错误")
    #     await self.code_service.relation_code(
    #         prefix, start_code_id, end_code_id, activity_num, activity_prizes
    #     )
    #     return ResultAPI()

    async def add_guaguale_activity(self, add_in: ActivityAddInSchema):
        """
        添加刮刮卡类型活动
        1，创建活动
        2，创建关联表-码号和奖品
        3，分配奖品（根据活动配置，将奖品分配到码号）
        """
        acitvity_code_no_list = add_in.activity_code_no_list
        activity_prize_list = add_in.activity_prize_list
        activity_num = add_in.num
        # 1检测码号是否可用

        # 创建活动
        data = add_in.model_dump(exclude={"activity_prize_list"})
        if add_in.rules_desc:
            data["rules_desc"] = ",".join(
                item for item in add_in.rules_desc if item is not None and item != ""
            )

        # 创建活动
        activity = await self.activity_orm.create(data)
        if not activity:
            raise AppException(exc=HttpResp.FAILED, msg="创建失败")

        # 创建活动附属表
        try:
            # 奖品
            activity_prize_list_data = []
            for activity_prize in activity_prize_list:
                data = activity_prize.model_dump()
                data["activity_id"] = activity.id
                activity_prize_list_data.append(data)
            await self.activity_prize_orm.bulk_create(activity_prize_list_data)
            # 码编号
            acitvity_code_no_list_data = []
            for acitvity_code_no in acitvity_code_no_list:
                data = acitvity_code_no.model_dump()
                data["activity_id"] = activity.id
                acitvity_code_no_list_data.append(data)
            await self.activity_code_no_orm.bulk_create(acitvity_code_no_list_data)

            #  关联码号奖品
            await self.code_service.rellot_prize(activity_id=activity.id)

        except AppException as e:
            await self.activity_orm.delete_by_id(activity.id)
            raise AppException(exc=HttpResp.FAILED, msg=e.msg)
        return ResultAPI()

    async def edit(self, edit_in: ActivityEditInSchema):
        """
        更新活动信息

        0，判断是否能更新
        1，更新活动信息
        2，更新关联的奖品
        3，更新关联的码号
        4，更新关联的码号奖品

        """
        activity_id = edit_in.id
        activity = await self.activity_orm.get_one_by_id(activity_id)
        if not activity:
            raise AppException(exc=HttpResp.FAILED, msg="活动不存在")
        if activity.lock == 1:
            raise AppException(exc=HttpResp.FAILED, msg="活动已锁定，不能修改")
        # 删除活动奖品中间表数据
        # await self.cancel_activity_relation_prizes(activity_id)
        # await self.activity_prize_orm.delete_by_filters(activity_id=activity_id)

        # 释放码号（前提是码号未开始使用）
        # await self.code_orm.reset_codes(activity_id)

        # 更新活动
        data = edit_in.model_dump(exclude={"num"})
        # activity_num = edit_in.num
        # activity_prizes = edit_in.activity_prize_list
        if edit_in.rules_desc:
            data["rules_desc"] = ",".join(item for item in edit_in.rules_desc)

        await self.activity_orm.update(id=activity_id, data=data)

        # 更新活动奖品中间表
        # for activity_prize in activity_prizes:
        #     activity_prize.activity_id = activity_id
        #     statement = insert(ActivityPrizeModel).values(**activity_prize.model_dump())
        #     await db.execute(statement)

        # 更新码号奖品
        # prefix = edit_in.prefix
        # start_code_id = edit_in.start_code_id
        # end_code_id = edit_in.end_code_id
        # if not prefix or not start_code_id or not end_code_id:
        #     raise AppException(exc=HttpResp.FAILED, msg="卡片参数错误")

        # await self.code_service.rellot_prize(activity.id)

        return ResultAPI()

    async def detail(self, detail_in: ActivityDetailInSchema):
        """
        抽奖活动详情
        """
        activity = await self.activity_orm.get_one_by_id(detail_in.id)
        if not activity:
            raise AppException(exc=HttpResp.FAILED, msg="活动不存在")
        activity_data = activity.model_dump(exclude={"activity_prize_list"})

        # 获取关联奖品
        activity_prize_list = await self.activity_prize_orm.get_all_by_filters(
            activity_id=detail_in.id
        )

        # 获取关联的码号批次
        activity_code_no_list = await self.activity_code_no_orm.get_all_by_filters(
            activity_id=activity.id
        )
        activity_code_no_list_data = [
            activity_code_no.model_dump() for activity_code_no in activity_code_no_list
        ]
        for item in activity_code_no_list_data:
            item["text"] = (
                f"{item['prefix']}.{item['code_id_start']}~{item['code_id_end']}  计数：{item['total_num']}"
            )
        activity_data["activity_code_no_list"] = activity_code_no_list_data
        activity_data["activity_prize_list"] = activity_prize_list

        activity_detail_out_data = ActivityDetailOutSchema(**activity_data)

        return ResultAPI(data=activity_detail_out_data)

    async def delete(self, delete_in: ActivityDeleteInSchema):
        """
        废弃-暂不设置删除活动的功能
        作废活动替代-释放码号（）
        """

        # 删除关联奖品 关联表
        # await self.cancel_activity_relation_prizes(delete_in.id)
        await self.activity_prize_orm.delete_by_filters(activity_id=delete_in.id)
        # 取消关联码号的关联
        await self.code_orm.reset_codes(activity_id=delete_in.id)

        # 删除活动
        activity_statement = delete(ActivityModel).where(
            ActivityModel.id == delete_in.id
        )

        await db.execute(activity_statement)
        return ResultAPI()

    async def status(self, status_in: ActivityStatusInSchema):
        statement = (
            update(ActivityModel)
            .where(ActivityModel.id == status_in.id)
            .values(status_in.model_dump())
        )
        await db.execute(statement)
        return ResultAPI()

    async def get_activity_residue_count(self, activity_id: int):
        """
        根据活动id获取剩余抽奖次数
        :param activity_id:
        :return: residue_count
        logic:
        1，获取活动信息根据id查询
        2，判断是类型：刮刮乐，二维码抽奖
        3.1，刮刮乐，根据活动id查询活动码号（未使用）数量。
        3.2，二维码抽奖，根据活动id查询活动抽中临时奖品数量，总数减去他，返回剩余次数
        """
        activity = await self.activity_orm.get_one_by_id(activity_id)
        if not activity:
            raise AppException(exc=HttpResp.FAILED, msg="活动不存在")
        # 活动名额总数
        activity_count = activity.num
        if activity_count is None:
            return 0
        if activity.type == ActivityTypeEnum.GUAGUALE.value:
            # 获取code未使用的code数量

            unused_code_count = await self.code_orm.count_by_filters(
                activity_id=activity_id, status=CodeStatusEnum.BOUND.value
            )
            return unused_code_count
        elif activity.type == ActivityTypeEnum.QRCODE.value:
            # 获取相关活动订单数，
            where = (
                func.json_extract(ActivityOrderModel.activity,
                                  "$.id").cast(Integer)
                == activity_id
            )
            activity_order_count = await self.activity_order_orm.count_by_filters(
                where=where
            )

            return activity_count - int(activity_order_count)
        else:
            raise AppException(exc=HttpResp.FAILED, msg="活动类型错误")

    async def invalidate_activity(self, activity_id: int):
        """
        作废活动
        1，更新活动信息
        2，将码号关联的信息重置（奖品，状态，绑定的活动信息）
        """
        await self.activity_orm.update(activity_id, {"lock": 1, "status": 0})
        await self.code_orm.reset_codes(activity_id)
        return ResultAPI()
